package org.rainbowinverted.charging.device.connect.ykc.server;

import org.rainbowinverted.charging.device.connect.ykc.server.decoder.YkcDecoder;
import org.rainbowinverted.charging.device.connect.ykc.server.decoder.YkcProtocolDecoder;
import org.rainbowinverted.charging.device.connect.ykc.server.encoder.YkcEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.Slf4JLoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

/**
 * 云快充server
 * @author hehuaiping
 */
@Slf4j
@Component
public class SeducYkcDeviceConnectServer implements ApplicationRunner {
    private final YkcDecoder ykcDecoder;
    private final YkcEncoder ykcEncoder;
    private ChannelFuture channelFuture;

    @Value("${rainbowinverted.device-connect.netty.port:7085}")
    private int ykcDevicePort;

    public SeducYkcDeviceConnectServer(YkcDecoder ykcDecoder,
                                       YkcEncoder ykcEncoder) {
        this.ykcDecoder = ykcDecoder;
        this.ykcEncoder = ykcEncoder;
    }


    public synchronized void startServer() {
        InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE);
        if (channelFuture == null || !channelFuture.channel().isOpen()) {
            NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
            NioEventLoopGroup workGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);
            try {
                ServerBootstrap server = new ServerBootstrap();
                // 内存池化
                ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
                server.group(bossGroup, workGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.ALLOCATOR, allocator)
                        .childOption(ChannelOption.ALLOCATOR, allocator)
                        .childOption(ChannelOption.SO_RCVBUF, 2048)
                        .childOption(ChannelOption.SO_SNDBUF, 2048)
                        .childOption(ChannelOption.TCP_NODELAY, true)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) {
                                ch.pipeline()
                                        .addLast("loggingHandler", new LoggingHandler(LogLevel.DEBUG))
                                        .addLast("readTimeoutHandler", new ReadTimeoutHandler(120))
                                        .addLast("ykcProtocolDecoder", new YkcProtocolDecoder())
                                        .addLast("ykcEncoder", ykcEncoder)
                                        .addLast("ykcDecoder", ykcDecoder);
                            }
                        });
                this.channelFuture = server.bind(ykcDevicePort).sync();
                log.info("start ykc device connect server success");
                // 优雅停机
                channelFuture.channel().closeFuture().sync();
            }catch (Exception e){
                log.error("exception", e);
                try {
                    bossGroup.shutdownGracefully().sync();
                    workGroup.shutdownGracefully().sync();
                } catch (Exception e1) {
                    log.error("exception", e1);
                }
            }
        }
    }

    @Override
    public void run(ApplicationArguments args) {
        startServer();
    }
}
